package com.zhy.example.druid.demo.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhy
 * @version 1.0.0
 * @ClassName LoadDataInFileUtil.java
 * @Description 使用LOAD DATA LOCAL INFILE实现大批量插入：https://blog.csdn.net/baidu_38083619/article/details/83378885
 * <p>
 * MySQL使用LOAD DATA LOCAL INFILE从文件中导入数据比insert语句要快，MySQL文档上说要快20倍左右。
 * 但是这个方法有个缺点，就是导入数据之前，必须要有文件，也就是说从文件中导入。这样就需要去写文件，以及文件删除等维护。某些情况下，比如数据源并发的话，还会出现写文件并发问题，很难处理。
 * 那么有没有什么办法，可以达到同样的效率，直接从内存（IO流中）中导入数据，而不需要写文件呢？
 * <p>
 * <p>
 * MySQL社区提供这样一个方法：setLocalInfileInputStream（），此方法位于com.mysql.jdbc.PreparedStatement 类中。
 * 通过使用 MySQL JDBC 的setLocalInfileInputStream 方法实现从Java InputStream中load data local infile 到MySQL数据库中。
 * @createTime 2020年11月03日 11:05:00
 */
@Component
public class LoadDataInFileUtil {

    private Logger logger = LoggerFactory.getLogger(LoadDataInFileUtil.class);
    private Connection conn = null;
    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 将数据从输入流加载到MySQL。
     *
     * @param loadDataSql  SQL语句
     * @param dataStream   输入流
     * @param jdbcTemplate
     * @return int 成功插入的行数
     */
    private int bulkLoadFromInputStream(String loadDataSql, InputStream dataStream, JdbcTemplate jdbcTemplate) throws SQLException {
        if (null == dataStream) {
            logger.info("输入流为NULL，没有数据导入");
            return 0;
        }
        conn = jdbcTemplate.getDataSource().getConnection();
        PreparedStatement preparedStatement = conn.prepareStatement(loadDataSql);
        int result = 0;
//        if (preparedStatement.isWrapperFor(com.mysql.jdbc.Statement.class)) {
//            com.mysql.jdbc.PreparedStatement mysqlStatement = preparedStatement.unwrap(com.mysql.jdbc.PreparedStatement.class);
//            mysqlStatement.setLocalInfileInputStream(dataStream);
//            result = mysqlStatement.executeUpdate();
//        }
        return result;
    }

    /**
     * 组装 SQL 语句。
     *
     * @param dataBaseName 数据库名。
     * @param tableName    表名。
     * @param columnName   要插入数据的列名。
     */
    public String assembleSql(String dataBaseName, String tableName, String columnName[]) {
        String insertColumnName = StringUtils.join(columnName, ",");
        String sql = "LOAD DATA LOCAL INFILE 'sql.csv' IGNORE INTO TABLE " + dataBaseName + "." + tableName + "(" + insertColumnName + ")";
        return sql;
    }

    /**
     * 往 StringBuilder 里追加数据。
     *
     * @param builder StringBuilder。
     * @param object  数据。
     */
    public void builderAppend(StringBuilder builder, String object) {
        builder.append(object);
        builder.append("\t");
    }

    /**
     * 往 StringBuilder 里追加一条数据的最后一个字段。
     *
     * @param builder StringBuilder。
     * @param object  数据。
     */
    public void builderEnd(StringBuilder builder, Object object) {
        builder.append(object);
        builder.append("\n");
    }

    /**
     * 通过 LOAD DATA LOCAL INFILE 大批量导入数据到 MySQL。
     *
     * @param sql     SQL语句。
     * @param builder 组装好的数据。
     */
    public int fastInsertData(String sql, StringBuilder builder) {
        int rows = 0;
        InputStream is = null;
        try {
            byte[] bytes = builder.toString().getBytes();
            if (bytes.length > 0) {
                is = new ByteArrayInputStream(bytes);
                //批量插入数据。
                long beginTime = System.currentTimeMillis();
                rows = bulkLoadFromInputStream(sql, is, jdbcTemplate);
                long endTime = System.currentTimeMillis();
                logger.info("LOAD DATA LOCAL INFILE :【插入" + rows + "行数据至MySql中，耗时" + (endTime - beginTime) + "ms。】");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
                if (null != conn) {
                    conn.close();
                }
            } catch (IOException | SQLException e) {
                e.printStackTrace();
            }
        }
        return rows;
    }

    public static void main(String[] args) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        map.put("11", "1");
    }
}
